
// Copyright (C) 2012 Luca Piccioni
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//  
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//  
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

using System;
using System.Xml;

using Derm.Render;

namespace Derm.Xml.Collada
{
	/// <summary>
	/// Interface implemented by Collada transform elements.
	/// </summary>
	public interface IColladaTransform
	{
		/// <summary>
		/// Affine a transformation matrix.
		/// </summary>
		/// <param name="modelMatrix">
		/// A <see cref="ModelMatrix"/> representing the current transformation.
		/// </param>
		/// <returns>
		/// It returns the affined transformation matrix.
		/// </returns>
		ModelMatrix AffineTransform(ModelMatrix modelMatrix);
	}

	/// <summary>
	/// The lookat element contains a position and orientation transformation suitable for aiming a camera.
	/// The lookat element contains three mathematical vectors within it that describe: 
	/// 1.	The position of the object;
	/// 2.	The position of the interest point;
	/// 3.	The direction that points up.
	/// </summary>
	public class ColladaTransformLookAt : ColladaXmlSerializable, IColladaTransform
	{
		#region Constructors

		/// <summary>
		/// Construct a ColladaTransformLookAt.
		/// </summary>
		/// <param name="parent">
		/// The parent element containing this ColladaTransformLookAt.
		/// </param>
		public ColladaTransformLookAt(ColladaXmlSerializable parent) : base(parent)
		{
			
		}

		#endregion

		#region Transform LookAt Information

		public Vertex3f Position;

		public Vertex3f Target;

		public Vertex3f UpVector;

		#endregion

		#region ColladaXmlSerializable Overrides

		/// <summary>
		/// Read the element text of the current XML serializable.
		/// </summary>
		/// <param name="xmlReader">
		/// A <see cref="XmlReader"/> used for reading child element. It is positioned at the start element.
		/// </param>
		public override void ReadText(XmlReader xmlReader)
		{
			if (xmlReader  == null)
				throw new ArgumentNullException("xmlReader");

			float[] value = XmlHelper.ReadXmlListOfFloats(xmlReader);

			if (value.Length != 9)
				throw new InvalidOperationException("requires 16 float items");
		}

		#endregion

		#region IColladaTransform Implementation

		/// <summary>
		/// Affine a transformation matrix.
		/// </summary>
		/// <param name="modelMatrix">
		/// A <see cref="ModelMatrix"/> representing the current transformation.
		/// </param>
		/// <returns>
		/// It returns the affined transformation matrix.
		/// </returns>
		public ModelMatrix AffineTransform(ModelMatrix modelMatrix)
		{
			if (modelMatrix == null)
				throw new ArgumentNullException("modelMatrix");

			return (modelMatrix);
		}

		#endregion
	}

	/// <summary>
	/// Matrix transformations embody mathematical changes to points within a coordinate systems or the 
	/// coordinate system itself. The matrix element contains a 4-by-4 matrix of floating-point values.
	/// </summary>
	public class ColladaTransformMatrix : ColladaXmlSerializable, IColladaTransform
	{
		#region Constructors

		/// <summary>
		/// Construct a ColladaTransformMatrix.
		/// </summary>
		/// <param name="parent">
		/// The parent element containing this ColladaTransformMatrix.
		/// </param>
		public ColladaTransformMatrix(ColladaXmlSerializable parent) : base(parent)
		{
			
		}

		#endregion

		#region Transform Matrix Information

		public ModelMatrix Matrix;

		#endregion

		#region ColladaXmlSerializable Overrides

		/// <summary>
		/// Read the element text of the current XML serializable.
		/// </summary>
		/// <param name="xmlReader">
		/// A <see cref="XmlReader"/> used for reading child element. It is positioned at the start element.
		/// </param>
		public override void ReadText(XmlReader xmlReader)
		{
			if (xmlReader  == null)
				throw new ArgumentNullException("xmlReader");

			float[] value = XmlHelper.ReadXmlListOfFloats(xmlReader);

			if (value.Length != 16)
				throw new InvalidOperationException("requires 16 float items");

			Matrix = new ModelMatrix();
			Matrix[0, 0] = value[0];
			Matrix[0, 1] = value[1];
			Matrix[0, 2] = value[2];
			Matrix[0, 3] = value[3];

			Matrix[1, 0] = value[4];
			Matrix[1, 1] = value[5];
			Matrix[1, 2] = value[6];
			Matrix[1, 3] = value[7];

			Matrix[2, 0] = value[8];
			Matrix[2, 1] = value[9];
			Matrix[2, 2] = value[10];
			Matrix[2, 3] = value[11];

			Matrix[3, 0] = value[12];
			Matrix[3, 1] = value[13];
			Matrix[3, 2] = value[14];
			Matrix[3, 3] = value[15];
		}

		#endregion

		#region IColladaTransform Implementation

		/// <summary>
		/// Affine a transformation matrix.
		/// </summary>
		/// <param name="modelMatrix">
		/// A <see cref="ModelMatrix"/> representing the current transformation.
		/// </param>
		/// <returns>
		/// It returns the affined transformation matrix.
		/// </returns>
		public ModelMatrix AffineTransform(ModelMatrix modelMatrix)
		{
			if (modelMatrix == null)
				throw new ArgumentNullException("modelMatrix");

			return (modelMatrix);
		}

		#endregion
	}

	/// <summary>
	/// The rotate element contains an angle and a mathematical vector that represents the axis of rotation.
	/// </summary>
	public class ColladaTransformRotate : ColladaXmlSerializable, IColladaTransform
	{
		#region Constructors

		/// <summary>
		/// Construct a ColladaTransformRotate.
		/// </summary>
		/// <param name="parent">
		/// The parent element containing this ColladaTransformRotate.
		/// </param>
		public ColladaTransformRotate(ColladaXmlSerializable parent) : base(parent)
		{
			
		}

		#endregion

		#region Transform Rotate Information

		public Quaternion Rotation;

		#endregion

		#region ColladaXmlSerializable Overrides

		/// <summary>
		/// Read the element text of the current XML serializable.
		/// </summary>
		/// <param name="xmlReader">
		/// A <see cref="XmlReader"/> used for reading child element. It is positioned at the start element.
		/// </param>
		public override void ReadText(XmlReader xmlReader)
		{
			if (xmlReader  == null)
				throw new ArgumentNullException("xmlReader");

			float[] value = XmlHelper.ReadXmlListOfFloats(xmlReader);

			if (value.Length != 4)
				throw new InvalidOperationException("requires 4 float items");

			Rotation = new Quaternion(new Vertex3f(value[0], value[1], value[2]), value[3]);
		}

		#endregion

		#region IColladaTransform Implementation

		/// <summary>
		/// Affine a transformation matrix.
		/// </summary>
		/// <param name="modelMatrix">
		/// A <see cref="ModelMatrix"/> representing the current transformation.
		/// </param>
		/// <returns>
		/// It returns the affined transformation matrix.
		/// </returns>
		public ModelMatrix AffineTransform(ModelMatrix modelMatrix)
		{
			if (modelMatrix == null)
				throw new ArgumentNullException("modelMatrix");

			modelMatrix.Rotate(Rotation);

			return (modelMatrix);
		}

		#endregion
	}

	/// <summary>
	/// The scale element contains a mathematical vector that represents the relative proportions of the X, Y and Z axes of a coordinated system.
	/// </summary>
	public class ColladaTransformScale : ColladaTargetableFloat3, IColladaTransform
	{
		#region Constructors

		/// <summary>
		/// Construct a ColladaTransformScale.
		/// </summary>
		/// <param name="parent">
		/// The parent element containing this ColladaTransformScale.
		/// </param>
		public ColladaTransformScale(ColladaXmlSerializable parent) : base(parent)
		{
			
		}

		#endregion

		#region IColladaTransform Implementation

		/// <summary>
		/// Affine a transformation matrix.
		/// </summary>
		/// <param name="modelMatrix">
		/// A <see cref="ModelMatrix"/> representing the current transformation.
		/// </param>
		/// <returns>
		/// It returns the affined transformation matrix.
		/// </returns>
		public ModelMatrix AffineTransform(ModelMatrix modelMatrix)
		{
			if (modelMatrix == null)
				throw new ArgumentNullException("modelMatrix");

			modelMatrix.Scale(Value);

			return (modelMatrix);
		}

		#endregion
	}

	/// <summary>
	/// The skew element contains an angle and two mathematical vectors that represent the axis of rotation and the axis of translation.
	/// </summary>
	public class ColladaTransformSkew : ColladaXmlSerializable, IColladaTransform
	{
		#region Constructors

		/// <summary>
		/// Construct a ColladaTransformSkew.
		/// </summary>
		/// <param name="parent">
		/// The parent element containing this ColladaTransformSkew.
		/// </param>
		public ColladaTransformSkew(ColladaXmlSerializable parent) : base(parent)
		{
			
		}

		#endregion

		#region Transform Skew Information

		public Quaternion Rotation;

		public Vertex3f Translation;

		#endregion

		#region ColladaXmlSerializable Overrides

		/// <summary>
		/// Read the element text of the current XML serializable.
		/// </summary>
		/// <param name="xmlReader">
		/// A <see cref="XmlReader"/> used for reading child element. It is positioned at the start element.
		/// </param>
		public override void ReadText(XmlReader xmlReader)
		{
			if (xmlReader  == null)
				throw new ArgumentNullException("xmlReader");

			float[] value = XmlHelper.ReadXmlListOfFloats(xmlReader);

			if (value.Length != 7)
				throw new InvalidOperationException("requires 7 float items");

			Rotation = new Quaternion(new Vertex3f(value[1], value[2], value[3]), value[0]);
			Translation = new Vertex3f(value[4], value[5], value[6]);
		}

		#endregion

		#region IColladaTransform Implementation

		/// <summary>
		/// Affine a transformation matrix.
		/// </summary>
		/// <param name="modelMatrix">
		/// A <see cref="ModelMatrix"/> representing the current transformation.
		/// </param>
		/// <returns>
		/// It returns the affined transformation matrix.
		/// </returns>
		public ModelMatrix AffineTransform(ModelMatrix modelMatrix)
		{
			if (modelMatrix == null)
				throw new ArgumentNullException("modelMatrix");

			return (modelMatrix);
		}

		#endregion
	}

	/// <summary>
	/// The translate element contains a mathematical vector that represents the distance along the X, Y and Z-axes.
	/// </summary>
	public class ColladaTransformTranslate : ColladaTargetableFloat3, IColladaTransform
	{
		#region Constructors

		/// <summary>
		/// Construct a ColladaTransformTranslate.
		/// </summary>
		/// <param name="parent">
		/// The parent element containing this ColladaTransformTranslate.
		/// </param>
		public ColladaTransformTranslate(ColladaXmlSerializable parent) : base(parent)
		{
			
		}

		#endregion

		#region IColladaTransform Implementation

		/// <summary>
		/// Affine a transformation matrix.
		/// </summary>
		/// <param name="modelMatrix">
		/// A <see cref="ModelMatrix"/> representing the current transformation.
		/// </param>
		/// <returns>
		/// It returns the affined transformation matrix.
		/// </returns>
		public ModelMatrix AffineTransform(ModelMatrix modelMatrix)
		{
			if (modelMatrix == null)
				throw new ArgumentNullException("modelMatrix");

			modelMatrix.Translate(Value);

			return (modelMatrix);
		}

		#endregion
	}
}
